/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package overload;

import java.util.LinkedList;
import java.util.Random;
import org.uncommons.maths.*;
import org.uncommons.maths.random.ExponentialGenerator;
import org.uncommons.maths.random.MersenneTwisterRNG;

/**
 *
 * @author benjamin
 */
public class Sim {
    
    
    //paramètres à gérer
    public static boolean overLoadControl;
    public static int limitTime=15000000; //100 000 = 10 s
    public static double callRate=0.022500; //0.01correspond à 100 appels par seconde
    public static double callDuration = 0.000001111; //0.000001111 = 90s    

    //variables 
    public static int nbSecProcUse=0;
    public static int nbSecProcIdle;
    public static LinkedList<Appel> buffer;
    public static LinkedList<Appel> bufferCallRunning;
    public static LinkedList<Appel> bufferCallToBeFinished;
    public static LinkedList<Appel> bufferReattempt1;
    public static LinkedList<Appel> bufferReattempt2;
    public static LinkedList<Appel> bufferReattempt1Out;
    public static LinkedList<Appel> bufferReattempt2Out;
    public static int processDurationRemaining=0;
    public static double[] controlLevel=new double[9];
    public static int controlLevelCurrent=0;
    public static int callSetupDelay=0;
    public static int nbReattempted=0;
    public static int nbReattemptedSuccess=0;
    public static int NC=1125;
    public static int NI=1125;
    public static int DeltaN=20;

    public Sim(double CallRatio, boolean overload){
        callRate=CallRatio;
        overLoadControl=overload;
        nbSecProcUse=0;
       nbSecProcIdle=0;
        processDurationRemaining=0;
        controlLevel=new double[9];
        controlLevelCurrent=0;
        callSetupDelay=0;
         nbReattempted=0;
         nbReattemptedSuccess=0;
         NC=1125;
         NI=1125;
         DeltaN=20;
       
        controlLevel[0]=1.0;
        controlLevel[1]=0.84;
        controlLevel[2]=0.68;
        controlLevel[3]=0.52;
        controlLevel[4]=0.37;
        controlLevel[5]=0.25;
        controlLevel[6]=0.15;
        controlLevel[7]=0.05;
        controlLevel[8]=0.01;

        
       buffer=new LinkedList();
       bufferCallRunning=new LinkedList<Appel>();
       bufferCallToBeFinished=new LinkedList<Appel>();
       bufferReattempt1=new LinkedList<Appel>();
       bufferReattempt2=new LinkedList<Appel>();
       bufferReattempt1Out=new LinkedList<Appel>();
       bufferReattempt2Out=new LinkedList<Appel>();
    }
    
    public void execute() {
        
       

       //generation de la distribution exponentielle pour la durée des appels et les arrivées d'appels
        Random rng = new MersenneTwisterRNG();
        ExponentialGenerator gen = new ExponentialGenerator(callRate, rng);
        int n=(int) (gen.nextValue().intValue());
        //System.out.println("N= "+n);
    
        
        
       ExponentialGenerator gen2 = new ExponentialGenerator(callDuration, rng);
            
       /*--------------------------------------------------------
        * -----------------------------Debut de la boucle temporelle-------------
        * --------------------------------------------------------------------------------------*/
        double ratioUse=0;
        double ratioUseAj=0;
        double ratioUseAjPre=0;
        int nbSecProcUseAj=0;
        int ij=0;
        boolean preStateOver=false;
        boolean overload=false;

        for(int i=0;i<limitTime;i++){
            
            ij++;    

            //Overload Control ajustement
            if(Sim.overLoadControl==true){
                
                    if(i%200000 == 0){
                         ratioUseAjPre=ratioUseAj;
                         ratioUseAj=(double)(nbSecProcUseAj)/(double)(ij);
                        // System.out.println("NbSecProcUseAJ "+nbSecProcUseAj);
                        // System.out.println("ij "+ij);
                        // System.out.println("Taux d'usage proc"+tauxUsageAj);
                         nbSecProcUseAj=0;
                         ij=0;
                         //calculus of processesor ratio use during the interall
                         if(ratioUseAj>0.90){
                             overload=true;
                         }
                                                  
                         if(ratioUseAj>0.91  && preStateOver==false || ratioUseAj>0.89 && preStateOver==true ){
                          if(NC-DeltaN>=0){
                              NC=NC-DeltaN;
                          }else{
                              NC=0;
                          }
                        }else if(ratioUseAj<0.89 && preStateOver==true ){
                          NC=NC+DeltaN;
                        }
                         
                      if(ratioUseAj<0.89 && ratioUseAjPre<0.89){
                             preStateOver=true;
                         }else{
                             preStateOver=false;
                         }
                     }
                    
                    //Level Gestion
                       if(i%50000==0 ){
                          // System.out.println("Nombre appel Interval"+Appel.NOMBRE_APPEL_INTERVAL);
                          // System.out.println("NC "+NC);
                           if(Appel.NUMBEROFCALLINTERVAL > NC){
                                   Sim.controlLevelCurrent=Math.min(4, Sim.controlLevelCurrent+1);
                                 //  System.out.println("Diminution du Taux d'acceptation au level :"+Sim.controlLevelCurrent);
                       }else{
                                   Sim.controlLevelCurrent=Math.max(0,Sim.controlLevelCurrent-1);
                                   //System.out.println("Augmentation du Taux d'acceptation au level :"+Sim.controlLevelCurrent);
                         }
                           Appel.NUMBEROFCALLINTERVAL=0;
                    }
             }
            
              //Generation of the call in a exponential way
            if(n==i){
                Appel temp= new Appel(i,(int) (gen2.nextValue().intValue()));

                if(Math.random()<=Sim.controlLevel[Sim.controlLevelCurrent]){
                    buffer.add(temp);
                    Appel.addAggrStat(i);
                }else{
                   Appel.NUMBEROFCALLINTERVAL--;
                   Appel.NUMBEROFREFUSEDCALLWITHOUTPROCESSING++;
                   temp.nbTry++;
                   if(Math.random()<0.9){
                        temp.setTimeReattempt1(i);
                        bufferReattempt1Out.add(temp);
                        processDurationRemaining=processDurationRemaining+(int)(5 +10*(temp.tolerance)/6000);
                   }
               }
               double timeBetweenCall=gen.nextValue();
                 n=(int) (timeBetweenCall + i +1);
                 Appel.inputQueue[(int)timeBetweenCall]++;
            }
            
            //Processing of retrieing failed call due to OverLoad Control            
            if(i%10==0){
                for(int j=0;j<bufferReattempt1Out.size();j++){
                    if(bufferReattempt1Out.get(j).timeBegin>=i){
                                    if(Math.random()<=Sim.controlLevel[Sim.controlLevelCurrent]){
                                        processDurationRemaining=processDurationRemaining+(int)(5 +10*(bufferReattempt1Out.get(j).tolerance)/6000); 
                                        buffer.add(bufferReattempt1Out.remove(j));
                                        
                                        Appel.addAggrStat(i);
                                        Appel.TIME_CALL_REATTEMPTED_PREVIOUS=Appel.TIME_CALL_REATTEMPTED_ACTIVE;
                                        Appel.TIME_CALL_REATTEMPTED_ACTIVE=i;
                                        //Appel.inputQueueReattempted[Appel.TIME_CALL_REATTEMPTED_ACTIVE-Appel.TIME_CALL_REATTEMPTED_PREVIOUS]++;
                                    }else{
                                        if(Math.random()<=0.4){
                                             bufferReattempt1Out.get(j).setTimeReattempt2(i);
                                             processDurationRemaining=processDurationRemaining+(int)(5 +10*(bufferReattempt1Out.get(j).tolerance)/6000);
                                            bufferReattempt2Out.add(bufferReattempt1Out.remove(j));
                                            
                                        }else{
                                            processDurationRemaining=processDurationRemaining+(int)(5 +10*( bufferReattempt1Out.get(j).tolerance)/6000);
                                            bufferReattempt1Out.remove(j);
                                            
                                        }
                                 }  
                       }
                }
                for(int j=0;j<bufferReattempt2Out.size();j++){
                           if(bufferReattempt2Out.get(j).timeBegin>=i){
                                    if(Math.random()<=Sim.controlLevel[Sim.controlLevelCurrent]){
                                         processDurationRemaining=processDurationRemaining+(int)(5 +10*( bufferReattempt2Out.get(j).tolerance)/6000);
                                        buffer.add(bufferReattempt2Out.remove(j));
                                        Appel.addAggrStat(i);
                                    }else{
                                         processDurationRemaining=processDurationRemaining+(int)(5 +10*( bufferReattempt2Out.get(j).tolerance)/6000);
                                        bufferReattempt2Out.remove(j);
                                    }
                        }
                }
         }            
            
          
            //just to print out the time during the simulation
            if(i%100000==0){System.out.println(" T = "+(double)(i)/10000);}
            
           /*  System.out.println(" T= : "+ i+ "  Nombre d'appel dans le buffer  : "+buffer.size() +" Nombre d'appel en Cours  : "+bufferAppelEnCours.size()+
                " Nombre d'appel Fini en attente  : "+  bufferAppelFini.size() );*/
                    
             
             //processing of the call rejected by the overload control
            //We try to add the previously innefective phone call in the FIFO
             if(i%10==0){
                   for(int j=0;j<bufferCallRunning.size();j++ ){
                       if(bufferCallRunning.get(j).timeEnd>=i){
                           Appel temp=bufferCallRunning.remove(j);
                           temp.finished=true;
                           buffer.add(temp);
                           Appel.addAggrStat(i);
                           Appel.addRealStat(i);
                                 
                       }
                   }
                   
                    for(int j=0; j< bufferReattempt1.size();j++){
                       if(bufferReattempt1.get(j).timeBegin>=i){
                             if(Math.random()<=Sim.controlLevel[Sim.controlLevelCurrent]){
                                 if(Math.random()<0.9){
                                    buffer.add(bufferReattempt1.remove(j));
                                    Appel.addAggrStat(i);
                                    Appel.TIME_CALL_REATTEMPTED_PREVIOUS=Appel.TIME_CALL_REATTEMPTED_ACTIVE;
                                    Appel.TIME_CALL_REATTEMPTED_ACTIVE=i;
                                 //   Appel.inputQueueReattempted[Appel.TIME_CALL_REATTEMPTED_ACTIVE-Appel.TIME_CALL_REATTEMPTED_PREVIOUS]++;
                                 }else{
                                     bufferReattempt1.get(j).giveUpCall();
                                     bufferReattempt1.remove(j);
                                 }
                             }else{
                                 bufferReattempt1.get(j).nbTry++;
                                 bufferReattempt1.get(j).setTimeReattempt2(i);
                                 bufferReattempt2.add(bufferReattempt1.remove(j));
                             }
                       }
                   }

                   for(int j=0; j< bufferReattempt2.size();j++){
                       if(bufferReattempt2.get(j).timeBegin>=i){
                             if(Math.random()<=Sim.controlLevel[Sim.controlLevelCurrent]){
                                 if(Math.random()<0.4){
                                    buffer.add(bufferReattempt2.remove(j));       
                                    Appel.addAggrStat(i);
                                 }else{
                                        bufferReattempt2.get(j).giveUpCall();
                                        bufferReattempt2.remove(j);      
                                 }
                             }else{
                                  bufferReattempt2.get(j).giveUpCall();
                                  bufferReattempt2.remove(j);                                 
                             }
                       }
                   }
            }
            
           
           //processing of the buffer to look for the call waiting for too long in the buffer, and thus innefective
            if(i%10==0){
                   for(int j=0; j< buffer.size();j++){
                       if( buffer.get(j).timeBegin+buffer.get(j).tolerance<=i && buffer.get(j).finished==false){
                           buffer.get(j).ineffective=true;
                           if(buffer.get(j).nbTry == 0){
                               if(Math.random()<0.9){
                                    buffer.get(j).nbTry++;
                                    buffer.get(j).setTimeReattempt1(i);
                                   // dureeProcessRestant=(int)(50 +100*(buffer.get(j).tolerance-2000)/6000);
                                     processDurationRemaining=processDurationRemaining+(int)(5 +10*(buffer.get(j).tolerance)/6000);
                                    bufferReattempt1.add(buffer.remove(j));
                                   
                                 //   System.out.println("Un appel est remis dans le buffer après 1 essai infructueux");
                               }else{
                                        buffer.get(j).giveUpCall();
                                        processDurationRemaining=processDurationRemaining+(int)(5 +10*(buffer.get(j).tolerance-2000)/6000);
                                       // System.out.println("Incrementation proce "+dureeProcessRestant);
                                        buffer.remove(j);
                            }
                           }else if(buffer.get(j).nbTry==1){
                                if(Math.random()<0.4){
                                    buffer.get(j).nbTry++;
                                    buffer.get(j).setTimeReattempt2(i);
                                  //   dureeProcessRestant=(int)(50 +100*(buffer.get(j).tolerance-2000)/6000);
                                    processDurationRemaining=processDurationRemaining+(int)(5 +10*(buffer.get(j).tolerance)/6000);
                                    bufferReattempt2.add(buffer.remove(j));
                                    
                                  //  System.out.println("Un appel est remis dans le buffer après 2 essais infructueux");
                                }else{
                                   buffer.get(j).giveUpCall();
                                   processDurationRemaining=processDurationRemaining+(int)(5 +10*(buffer.get(j).tolerance)/6000);
                                   //System.out.println("Incrementation proce "+dureeProcessRestant);
                                   buffer.remove(j);
                               }
                         }else if(buffer.get(j).nbTry >1){
                             buffer.get(j).giveUpCall();
                              processDurationRemaining=processDurationRemaining+(int)(5 +10*(buffer.get(j).tolerance)/6000);
                             // System.out.println("Incrementation proce "+dureeProcessRestant);
                             buffer.remove(j);
                        }
                      }
                   }
            }

           
            //processing of the buffer
            if(processDurationRemaining<=0){
                if(!buffer.isEmpty()){
                     Appel temp=buffer.remove();
                     if(temp.finished==false){
                        bufferCallRunning.add(temp);
                             callSetupDelay=callSetupDelay+(i-temp.timeCreation);
                         /*   System.out.println("On traite l'appel crée");*/
                   
                        //System.out.println("Delay: "+(temp.timePrisEnCharge-temp.timeCreation));
                        if(temp.nbTry==0 || temp.ineffective==false ){
                            temp.debutAppel(i);
                            processDurationRemaining=24;
                        }else if(temp.nbTry==1){
                            temp.debutAppel(i);
                            processDurationRemaining=24;
                        }else {
                           temp.debutAppel(i);
                           processDurationRemaining=24;
                        }
                }else{
                        temp.terminationCall();
                        processDurationRemaining=16;
                 }  
                }
            }else{
                processDurationRemaining--;
                nbSecProcUse++;
                nbSecProcUseAj++;
            }
                    
        }
        
        //statistics
        ratioUse=(double)(nbSecProcUse)/(double)(limitTime);
        double callCompletionRate=(double)(Appel.NUMBERPROCESSEDCALL)/(double)Appel.NUMBERCREATEDCALL;
        double averageCallSetupDelay=(double)(callSetupDelay)/(double)(Appel.NOMBEROFFORWARDEDCALL);
        double succesProbReattempted = (double)(Appel.NUMBEROFREATTEMPTEDCALL-Appel.NUMBEROFREATTEMPTEDANDFAILEDCALL)/(double)(Appel.NUMBEROFREATTEMPTEDCALL);
       
        System.out.println("Number of created call : "+Appel.NUMBERCREATEDCALL);
        System.out.println("Number of processed call : "+Appel.NUMBERPROCESSEDCALL);
        System.out.println("Number of given up call : "+Appel.NUMBEROFGIVENUPCALL);
        System.out.println("Number of refused call : "+Appel.NUMBEROFREFUSEDCALLWITHOUTPROCESSING);
        
        System.out.println("---------------------------------------------------------------------------------------------------------------------");
        System.out.println("Call Completion Rate "+callCompletionRate);
        System.out.println("Total processor ratio use  :"+ratioUse);
        System.out.println("Call Setup Delay in .1 ms : "+averageCallSetupDelay);
        System.out.println("Success Probablityes of Reattempted Call :"+succesProbReattempted);
        
        System.out.println("Number of reattempted call"+Appel.NUMBEROFREATTEMPTEDCALL);
        System.out.println("Number of reattempted and failed call"+Appel.NUMBEROFREATTEMPTEDANDFAILEDCALL);
         
         int v;
       /*  System.out.println("Input Queue");
          for(v=0;v<200;v++){
              System.out.println(Appel.inputQueue[v]);
          }
         System.out.println("Reattempted");
          for(v=0;v<200;v++){
              System.out.println(Appel.inputQueueReattempted[v]);
          }
         System.out.println("Input Call Realease");
          for(v=0;v<200;v++){
              System.out.println(Appel.inputCallRealease[v]);
          }
        System.out.println("Agree traffic");
         for(v=0;v<200;v++){
              System.out.println(Appel.agreTraffic[v]);
          }*/
         
        }

    }
